/* 
 MiniSynth.pde  |  Wampum Physics  |  2/2011  |  GPL
 
 wampumphysics@gmail.com
 http://code.google.com/p/wampumphysics/

 This is the code for a small Arduino based analog/digital hybrid synth.
 
 The analog circuit consists of a MC14046 PLL based VCO with straight voltage
 control as well as a possible audio signal input for f/v converter, and a
 simple passive filter/volume stage. 
 
 The Arduino controls a MCP4725 I2C DAC to generate control voltages, and 
 has a digital square wave oscillator output with independant pitch control.
 
 I have the synth set up with three modes. I've given each its own function
 to make it quick and easy to add/remove/change modes. Right now its modes
 are 'LFO' on osc1 (more of a staircase generator with the large increment),
 'sample and hold' on osc 1 (just a simple random note generator), and 'sample
 and hold' on bot oscillators. 
 
 I just borrowed the I2C stuff from Stephen Shulz for the DAC -
   www.maybevideodoes.de
 
 
*/

#include <Wire.h>

byte Program = 64;
byte Device = 96;
int value;
int tempVal;

int period;
int p_inc=0; 
int depth;
int offset;
int lfo_inc=200;

int pitch_a = A0;
int dur_pot = A2;
int depth_pot = A3;

int pitch_b = A1;
int offset_b = 0;
int val_b;
int freq=-100; 
int osc_b = 5;

int mode_sw = 6;
int debounce = 100;
int mode = 0;

int leds[] = {10, 11, 13};

void setup() {
  Wire.begin(); 
  pinMode(osc_b, OUTPUT);
  for (int i=0; i<2; i++) {
    pinMode(leds[i], OUTPUT);
  }
  digitalWrite(leds[mode], HIGH);
  pinMode(mode_sw, INPUT);
}


void lfo() {
  if ((offset >= depth) | (offset <= -depth)) lfo_inc = -lfo_inc;
  if (depth) offset += lfo_inc;
}

void s_h() {
  offset = random(-depth, depth);
}

void s_h_b() {
  offset_b = random(-depth, depth);
  s_h();
}


void checkMode() {
  if ((digitalRead(mode_sw) == HIGH) & (debounce >= 100)) {
    digitalWrite(leds[mode], LOW);
    mode++;
    if (mode == 3) mode = 0;
    digitalWrite(leds[mode], HIGH);
    debounce = 0;
    offset_b = 0;
  }
  debounce++;
}

void loop() {
  tempVal = constrain(map(analogRead(pitch_a),0,1024,200,4095)+offset,200,4095);
  if ((value-tempVal < 2) | (value-tempVal > 2)) {
    value = tempVal;
  }
  byte b1 = byte((value / 16));
  byte b2 = byte(value % 16);
  Wire.beginTransmission(Device);
  Wire.send(Program);
  Wire.send(b1);
  Wire.send(b2 << 4); 
  Wire.endTransmission();
  
  val_b = analogRead(A3);
  if ((val_b-freq < 2) | (val_b-freq > 2)) {
     freq = map(val_b, 0, 1024, 0, 5000);
     if (val_b < 5) {
       freq = 0;
       noTone(osc_b);
       delayMicroseconds(200);
     }
     else tone(5, freq+offset_b);
  }
  period = map(analogRead(dur_pot), 0, 1024, 1, 2000);
  depth = map(analogRead(depth_pot),0,1024,0,2500);
  
  checkMode();
  
  if (debounce > 10000) debounce = 10;
  
  if (p_inc >= period) {
    if (mode == 0) lfo();
    if (mode == 1) s_h();
    if (mode == 2) s_h_b();
    p_inc = 0;
  }
  p_inc++;
}
